#pragma once

#include <vex/core/contract/handle.h>

namespace vex {
    template<class TCore> struct VEX_ABSTRACT handle : public vex::core::contract::handle {
        typedef TCore core_type;
        virtual bool query_interface(TCore**) = 0;
        VEX_PROTECT_CONTRACT(handle);
    };

    namespace core {
        namespace detail {
            struct VEX_ABSTRACT nothing;
        }
    }

    /// <summary>
    /// Primary template for list of contracts extending the defined contract. Not defined.
    /// Supports extending up to 6 contracts. Recursive definition hides query_handle from
    /// extending contracts, therefore the partial specialization method is used.
    /// </summary>
    template<
        class T1 = ::vex::core::detail::nothing, 
        class T2 = ::vex::core::detail::nothing, 
        class T3 = ::vex::core::detail::nothing, 
        class T4 = ::vex::core::detail::nothing, 
        class T5 = ::vex::core::detail::nothing,
        class T6 = ::vex::core::detail::nothing,
        class T7 = ::vex::core::detail::nothing // last one is unused
    > 
    struct VEX_ABSTRACT extends;

    /// <summary>
    /// Specialization for empty base type lists
    /// </summary>
    template<> struct VEX_ABSTRACT extends<::vex::core::detail::nothing>
    {
        typedef ::vex::core::detail::nothing head_type;
        typedef ::vex::core::detail::nothing tail_type;
        enum { size = 0u };

        VEX_PROTECT_CONTRACT(extends);
    };

    template<class T1> 
    struct VEX_ABSTRACT extends<T1> 
        : public T1
    {
        typedef T1 head_type;
        typedef extends<> tail_type;
        enum { size = tail_type::size + 1 };

        using T1::query_handle;
        
        VEX_PROTECT_CONTRACT(extends);
    };

    template<class T1, class T2> 
    struct VEX_ABSTRACT extends<T1, T2> 
        : public T1, public T2
    {
        typedef T1 head_type;
        typedef extends<T2> tail_type;
        enum { size = tail_type::size + 1 };

        using T1::query_handle;
        using T2::query_handle;
        
        VEX_PROTECT_CONTRACT(extends);
    };

    template<class T1, class T2, class T3> 
    struct VEX_ABSTRACT extends<T1, T2, T3> 
        : public T1, public T2, public T3
    {
        typedef T1 head_type;
        typedef extends<T2, T3> tail_type;
        enum { size = tail_type::size + 1 };

        using T1::query_handle;
        using T2::query_handle;
        using T3::query_handle;

        VEX_PROTECT_CONTRACT(extends);
    };

    template<class T1, class T2, class T3, class T4> 
    struct VEX_ABSTRACT extends<T1, T2, T3, T4> 
        : public T1, public T2, public T3, public T4
    {
        typedef T1 head_type;
        typedef extends<T2, T3, T4> tail_type;
        enum { size = tail_type::size + 1 };

        using T1::query_handle;
        using T2::query_handle;
        using T3::query_handle;
        using T4::query_handle;
        
        VEX_PROTECT_CONTRACT(extends);
    };

    template<class T1, class T2, class T3, class T4, class T5> 
    struct VEX_ABSTRACT extends<T1, T2, T3, T4, T5> 
        : public T1, public T2, public T3, public T4, public T5
    {
        typedef T1 head_type;
        typedef extends<T2, T3, T4, T5> tail_type;
        enum { size = tail_type::size + 1 };

        using T1::query_handle;
        using T2::query_handle;
        using T3::query_handle;
        using T4::query_handle;
        using T5::query_handle;
        
        VEX_PROTECT_CONTRACT(extends);
    };

    template<class T1, class T2, class T3, class T4, class T5, class T6> 
    struct VEX_ABSTRACT extends<T1, T2, T3, T4, T5, T6> 
        : public T1, public T2, public T3, public T4, public T5, public T6
    {
        typedef T1 head_type;
        typedef extends<T2, T3, T4, T5, T6> tail_type;
        enum { size = tail_type::size + 1 };

        using T1::query_handle;
        using T2::query_handle;
        using T3::query_handle;
        using T4::query_handle;
        using T5::query_handle;
        using T6::query_handle;
        
        VEX_PROTECT_CONTRACT(extends);
    };

    /// <summary>
    /// Primary template to define a vex dual hierarchy aware contract.
    /// </summary>
    template< class TContract, class TExtends = extends<> > 
    struct VEX_ABSTRACT define_contract 
        : public TExtends
    {
        typedef TContract contract_type;
        typedef TExtends base_list_type;

        //struct VEX_ABSTRACT covariant_handle : public virtual vex::handle<TContract> {
        //    VEX_PROTECT_CONTRACT(covariant_handle);
        //};

        using base_list_type::query_handle;
        virtual bool query_handle(handle<TContract>**) = 0;

        VEX_PROTECT_CONTRACT(define_contract);
    };


    /// <summary>
    /// Specialization for empty base lists, does not attempt to re-introduce query_handle
    /// </summary>
    template<class TContract> 
    struct VEX_ABSTRACT define_contract< TContract, extends<> >
    {
        typedef TContract contract_type;
        typedef extends<> base_list_type;

        //struct VEX_ABSTRACT covariant_handle : public virtual vex::handle<TContract> {
        //    VEX_PROTECT_CONTRACT(covariant_handle);
        //};

        virtual bool query_handle(handle<TContract>**) = 0;

        VEX_PROTECT_CONTRACT(define_contract);
    };
}